/*******************************************************************************

                  LVGL 输入法之TIME导航输入法实现

*******************************************************************************/
#include "lvIME.h"
#ifdef SUPPORT_LV_IME_GUIDE

#include "lvIME_TGuide.h"
#include "GB2312_Utf8.h"
#include "TMenuBase.h" //TMenu_ToTGuiKey
#include <string.h>

//使用TIME时，需要指定等宽字体
#ifndef LV_IME_FONTS_DW
  extern const lv_font_t lv_font_GB2312_HanSansSC_NorDW;
  #define LV_IME_FONTS_DW lv_font_GB2312_HanSansSC_NorDW
#endif

//--------------------------TWin缓冲区转Unicode缓冲区----------------------------
//暂不支持着色
static void _WinBuf2Utf8Buf(struct _lvIME_Guide *pIME) //主结构
{
  //Unicode转码,以行为单位
  char *pUtf8 = pIME->Ut8Buf;
  unsigned long Utf8Len = sizeof(pIME->Ut8Buf);
  for(unsigned char Line = 0; Line < TIME_MNG_DISP_H; Line++){
    char *pGB2312 = TWin_pGetString(&pIME->Win, Line);
    //替换可能存在的结束符
    for(unsigned char Pos = 0; Pos < TIME_MNG_DISP_W; Pos++){
      if(pGB2312[Pos] == '\0') pGB2312[Pos] = ' ';
    }
    unsigned long CurLen = Code_GB2312ToUtf8(pGB2312, TIME_MNG_DISP_W,
                                             pUtf8, Utf8Len);
    if(CurLen >= (Utf8Len - 1)){//满了
      *(pUtf8 + Utf8Len - 1) = '\0';//强制结束字符
      break;
    }
    pUtf8 += CurLen;
    *pUtf8++ = '\n'; //增加换行字符
    CurLen++;
    Utf8Len -= CurLen;
  }
  *(pUtf8 - 1) = '\0'; //最后一行换行转为结束字符
}

//-------------------------------事件回调实现---------------------------------
static void _EventHandler(lv_event_t * e)
{
  lv_event_code_t Code = lv_event_get_code(e);
  lv_obj_t * obj = lv_event_get_target_obj(e);
  struct _lvIME_Guide *pIME = (struct _lvIME_Guide *)lv_event_get_user_data(e);

  unsigned char  Key = 0;
  //虚拟键盘区域获取键值
  if(Code == LV_EVENT_VALUE_CHANGED){
    if(obj == pIME->pKeyG) Key = lvIME_GetKeyG(obj);
    else Key = 0; //异常
  }
  //响应物理按键
  else if(Code == LV_EVENT_KEY) {
    Key = *(uint32_t *)lv_event_get_param(e);
  }
  else return; //其它不响应

  #ifdef SUPPORT_LV_IME_TOUCH  //切换输入法
  if((Key == LV_KEY_HOME) || (Key == LV_KEY_END)){
    lvIME_GuideDelete(pIME);
    lvIME_cbGuideToOtherIME(pIME, Key);
    return;
  }
  #endif

  //交给输入法处理
  signed char Resume = TImeMng_Key(&pIME->Ime, TMenu_ToTGuiKey(Key));
  if(Resume <= -2){//保存并退出
    pIME->pString[Code_GB2312ToUtf8(pIME->GB2312EditBuf, strlen(pIME->GB2312EditBuf),
                      pIME->pString, pIME->Size)] = '\0';
    pIME->pEndCb(pIME->pvEndCb, 1);
    return;
  }
  else if(Resume){//不保存退出
    pIME->pEndCb(pIME->pvEndCb, 0);
    return;
  }
  lvIME_GuideRefresh(pIME);//刷新显示
}

//----------------------------创建导航输入法函数---------------------------------
//返回是否成功初始化
signed char lvIME_GuideCreate(lv_obj_t * pParent,   //父窗口
                              struct _lvIME_Guide *pIME, //主结构
                              lvIME_EndCb_t pEndCb, //退出回调
                              void *pvEndCb,        //退出回调参数
                              char *pString,          //用户已存在的字符串,必须以'\0'结尾
                              TIme_String_t Size,     //字符串缓冲区允许大小+1
                              unsigned char DefaultType,   //默认输入法,<4
                              unsigned char TypeMask,//可使用的输入法类型
                              const char *pSignTbl, //挂接的符号表,为空时使用默认
                              TIme_String_t Cursor) //默认光标位置,-1时为最后
{
  //结构初始化
  memset(pIME, 0, sizeof(struct _lvIME_Guide));
  pIME->pEndCb = pEndCb;
  pIME->pvEndCb = pvEndCb;

  pIME->pString = pString;
  pIME->Size = Size;
  //创建一个大按钮，以避免文本时框时无按键响应等问题
  lv_obj_t *pBtn = lv_btn_create(pParent);
  lv_obj_remove_style_all(pBtn); //去除全部显示等
  lv_obj_set_size(pBtn, lv_pct(100), lv_pct(100)); //全宽
  lv_obj_set_flex_grow(pBtn, 1);  //高可以伸展到全部空间
  lv_obj_set_style_border_width(pBtn, 0, LV_PART_MAIN);  // 去边框
  lv_obj_set_style_outline_width(pBtn, 0, LV_PART_MAIN); // 去边界
  lv_obj_add_event_cb(pBtn, _EventHandler, LV_EVENT_CLICKED, pIME);//响应点击
  lv_obj_add_event_cb(pBtn, _EventHandler, LV_EVENT_KEY, pIME);//响应按键
  pIME->pObj = pBtn;
  //创建按钮上的显示区，局中创建导航输入法要显示的文本框，按键不同颜色用 LV_TXT_COLOR_CMD 实现
  lv_obj_t *pLabel = lv_label_create(pBtn);
  //lv_obj_set_style_border_width(pLabel, 0, LV_PART_MAIN);  // 去边框以适配小屏
  //lv_obj_set_style_outline_width(pLabel, 0, LV_PART_MAIN); // 去边界以适配小屏
  lv_obj_set_size(pLabel, TIME_MNG_DISP_W * 8, TIME_MNG_DISP_H * 32);
  lv_obj_align(pLabel, LV_ALIGN_TOP_MID, 0, 0);
  lv_label_set_text_static(pLabel, pIME->Ut8Buf);
  //lv_font_add(&LV_IME_FONTS_DW);  // 注册字体
  lv_obj_set_style_text_font(pLabel, &LV_IME_FONTS_DW, 0);  ////使用等宽字体解决转换后显示问题
  lv_obj_add_event_cb(pLabel, _EventHandler, LV_EVENT_CLICKED, pIME);
  lv_obj_add_event_cb(pLabel, _EventHandler, LV_EVENT_KEY, pIME);
  lv_obj_update_layout(pLabel); //强制更新布局以使大小生效
  //在编辑位置叠加焦点区以提示编辑位置(暂略)

  pIME->GB2312EditBuf[Code_Utf8ToGB2312(pString, strlen(pString), //转换为GB2312码编辑
                 pIME->GB2312EditBuf, LV_IME_GB2312_EDIT_BUF)] = '\0';
  TWin_InitWin(&pIME->Win, TIME_MNG_DISP_W,TIME_MNG_DISP_H,
               pIME->WinBuf, TIME_MNG_DISP_W * (TIME_MNG_DISP_H + 1));

  //创建虚拟键盘
  #ifdef SUPPORT_LV_IME_TOUCH
    pIME->pKeyG = lvIME_CreateKeyG(_EventHandler, pIME, 0x01); //仅禁删除键
  #else
    pIME->pKeyG = lvIME_CreateKeyG(_EventHandler, pIME, 0x07);//禁所有附加键
  #endif
  //最后创建输入法
  TImeMng_Init(&pIME->Ime, &pIME->Win, pIME->GB2312EditBuf, Size,
               DefaultType, TypeMask, pSignTbl, Cursor);
  //转换到缓冲区显示
  _WinBuf2Utf8Buf(pIME);
}

//----------------------------刷新导航输入法函数---------------------------------
//仅刷新显示区
void lvIME_GuideRefresh(struct _lvIME_Guide *pIME)
{
  //转换到缓冲区显示
  _WinBuf2Utf8Buf(pIME);
  //更新一下显示区
  lv_obj_invalidate(pIME->pObj);
}

//----------------------------删除自身---------------------------------
void lvIME_GuideDelete(struct _lvIME_Guide *pIME)
{
  TImeMng_Quit(&pIME->Ime);   //退出输入法
  lv_obj_delete(pIME->pObj);  //删除编辑器
  lv_obj_delete(pIME->pKeyG); //删除软键盘自已
}

#endif //SUPPORT_LV_IME_GUIDE
